<?php
/*
 * Author  : top-songshijie
 * Email   : 997031758@qq.com
 * DateTime: 2023/09/14 17:08
 */

namespace SSJ\LaravelHelper\Dao;

use ReflectionException;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Model as BaseModel;
use Illuminate\Contracts\Container\BindingResolutionException;

abstract class BaseDao
{

    /**
     * 插入数据
     *
     * @param array $data
     *
     * @return mixed
     * @throws BindingResolutionException
     */
    public function toCreate(array $data)
    {
        return $this->getModel()->create($data);
    }

    /**
     * 删除数据
     *
     * @param string|array $where
     *
     * @return mixed
     * @throws BindingResolutionException
     */
    public function toDelete($where)
    {
        if (!is_array($where)) {
            $where = [$this->getPk() => (int)$where];
        }

        return $this->getModel()->where($where)->delete();
    }

    /**
     * 更新数据
     *
     * @param string|array $where
     * @param array        $data
     *
     * @return mixed
     * @throws BindingResolutionException
     */
    public function toUpdate($where, array $data)
    {
        if (!is_array($where)) {
            $where = [$this->getPk() => (int)$where];
        }
        $this->getModel()->where($where)->update($data);

        return $this->getModel()->where($where)->first();
    }

    /**
     * 获取一条数据
     *
     * @param string|array $where
     * @param string       $field
     * @param array        $with
     * @param string       $order
     *
     * @return mixed
     * @throws BindingResolutionException
     */
    public function getFirst($where, string $field = '*', array $with = [], string $order = '')
    {
        if (!is_array($where)) {
            $where = [$this->getPk() => (int)$where];
        }

        return $this->getModel()->select($field)->where($where)
            ->when($with, function ($query) use ($with) {
                $query->with($with);
            })->when($order, function ($query) use ($order) {
                $query->orderByRaw($order);
            })->first();
    }

    /**
     * 获取一条数据（数组格式）
     *
     * @param string|array $where
     * @param string       $field
     * @param array        $with
     * @param string       $order
     *
     * @return array
     * @throws BindingResolutionException
     */
    public function getFirstArray($where, string $field = '*', array $with = [], string $order = '')
    {
        $m = $this->getFirst($where, $field, $with, $order);

        return $m ? $m->toArray() : [];
    }

    /**
     * 读取数据条数
     *
     * @param string|array $where
     *
     * @return int
     * @throws BindingResolutionException|ReflectionException
     */
    public function getCount($where)
    {
        if (!is_array($where)) {
            $where = [$this->getPk() => (int)$where];
        }

        return $this->search($where)->count();
    }

    /**
     * 获取列表数据
     *
     * @param array  $where
     * @param int    $page
     * @param int    $page_num
     * @param string $field
     * @param string $order
     * @param array  $with
     *
     * @return array
     * @throws BindingResolutionException
     * @throws ReflectionException
     */
    public function getList(array $where, int $page = 0, int $page_num = 0, string $field = '*', string $order = '', array $with = [])
    {
        $builder = $this->search($where);

        $total = $builder->count();
        $list  = $builder->select($field)->when($page && $page_num, function ($query) use ($page, $page_num) {
            $query->forPage($page, $page_num);
        })->when($order, function ($query) use ($order) {
            $query->orderByRaw($order);
        })->when($with, function ($query) use ($with) {
            $query->with($with);
        })->get();

        return [$total, $list];
    }

    /**
     * 获取列表数据（数组格式）
     *
     * @param array  $where
     * @param int    $page
     * @param int    $page_num
     * @param string $field
     * @param string $order
     * @param array  $with
     *
     * @return array
     * @throws BindingResolutionException
     * @throws ReflectionException
     */
    public function getListArray(array $where, int $page = 0, int $page_num = 0, string $field = '*', string $order = '', array $with = [])
    {
        [$total, $list] = $this->getList($where, $page, $page_num, $field, $order, $with);
        $list = $list->toArray();

        return [$total, $list];
    }

    /**
     * 列加法
     *
     * @param string|array $where
     * @param string       $column
     * @param string       $amount
     *
     * @return bool|int
     * @throws BindingResolutionException
     * @throws ReflectionException
     */
    public function toIncrement($where, $column, $amount)
    {
        if (!is_array($where)) {
            $where = [$this->getPk() => (int)$where];
        }

        return $this->search($where)->increment($column, $amount);
    }

    /**
     * 列减法
     *
     * @param string|array $where
     * @param string       $column
     * @param string       $amount
     *
     * @return bool|int
     * @throws BindingResolutionException
     * @throws ReflectionException
     */
    public function toDecrement($where, $column, $amount)
    {
        if (!is_array($where)) {
            $where = [$this->getPk() => (int)$where];
        }

        return $this->search($where)->decrement($column, $amount);
    }

    /**
     * 获取字段列
     *
     * @param string|array $where
     * @param string       $field
     * @param string       $key
     *
     * @return mixed
     * @throws BindingResolutionException
     * @throws ReflectionException
     */
    public function getPluck($where, string $field, string $key = null)
    {
        if (!is_array($where)) {
            $where = [$this->getPk() => (int)$where];
        }

        return $this->search($where)->pluck($field, $key);
    }

    /**
     * 获取字段列（数组格式）
     *
     * @param string|array $where
     * @param string       $field
     * @param string       $key
     *
     * @return mixed
     * @throws BindingResolutionException
     * @throws ReflectionException
     */
    public function getPluckArray($where, string $field, string $key = null)
    {
        return $this->getPluck($where, $field, $key)->toArray();
    }

    /**
     * 求和
     *
     * @param string|array $where
     * @param string       $field
     *
     * @return mixed
     * @throws BindingResolutionException
     * @throws ReflectionException
     */
    public function getSum($field, $where = [])
    {
        if (!is_array($where)) {
            $where = [$this->getPk() => (int)$where];
        }

        return $this->search($where)->sum($field);
    }

    /**
     * 查询单个字段
     *
     * @param string|array $where
     * @param string       $field
     *
     * @return mixed
     * @throws BindingResolutionException
     * @throws ReflectionException
     */
    public function getValue($field, $where = [])
    {
        if (!is_array($where)) {
            $where = [$this->getPk() => (int)$where];
        }

        return $this->search($where)->value($field);
    }

    /**
     * 设置当前模型
     *
     * @return string
     */
    abstract protected function setModel(): string;

    /**
     * 获取当前模型
     *
     * @return BaseModel
     * @throws BindingResolutionException
     */
    protected function getModel()
    {
        return app()->make($this->setModel());
    }

    /**
     * 获取主键
     *
     * @return string
     * @throws BindingResolutionException
     */
    protected function getPk()
    {
        return $this->getModel()->getKeyName();
    }

    /**
     * 构造搜索builder
     *
     * @param array $where
     *
     * @return BaseModel
     * @throws ReflectionException|BindingResolutionException
     */
    protected function search(array $where = [])
    {
        if (!$where) {
            return $this->getModel();
        }

        [$withWhere] = $this->getScopeWhere($where);
        if (!$withWhere) {
            return $this->getModel();
        }

        return $this->getModel()->scopes($withWhere);
    }

    /**
     * 获取作用域能用的where
     *
     * @param array $where
     *
     * @return array[]
     * @throws ReflectionException
     */
    private function getScopeWhere(array $where)
    {
        $withWhere   = [];
        $removeWhere = [];
        $responses   = new \ReflectionClass($this->setModel());
        foreach ($where as $fieldName => $fieldValue) {
            $method = 'scope' . Str::studly($fieldName);
            if ($responses->hasMethod($method)) {
                if (is_null($fieldValue)) {
                    $fieldValue = '';
                } elseif (is_array($fieldValue)) {
                    $fieldValue = [$fieldValue];
                }
                $withWhere[Str::camel($fieldName)] = $fieldValue;
            } else {
                $removeWhere[$fieldName] = $fieldValue;
            }
        }

        return [$withWhere, $removeWhere];
    }
}
